using Microsoft.EntityFrameworkCore;
using UniversalAdmin.Application.Dtos;
using UniversalAdmin.Application.Dtos.ArticleCategory;
using UniversalAdmin.Application.Services.Interfaces;
using UniversalAdmin.Domain.Entities.App;
using UniversalAdmin.Domain.Repositories;

namespace UniversalAdmin.Application.Services.Implementations;

/// <summary>
/// 文章分类服务实现类
/// </summary>
public class ArticleCategoryService : IArticleCategoryService
{
    private readonly IRepositories<ArticleCategory> _categoryRepository;
    private readonly IRepositories<Article> _articleRepository;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="categoryRepository">分类仓储</param>
    /// <param name="articleRepository">文章仓储</param>
    public ArticleCategoryService(
        IRepositories<ArticleCategory> categoryRepository,
        IRepositories<Article> articleRepository)
    {
        _categoryRepository = categoryRepository;
        _articleRepository = articleRepository;
    }

    public async Task<ArticleCategoryDto?> GetByIdAsync(Guid id)
    {
        var category = await _categoryRepository.Table
            .Include(c => c.Parent)
            .Include(c => c.Articles)
            .FirstOrDefaultAsync(c => c.Id == id && !c.IsDeleted);

        return category == null ? null : await MapToDtoAsync(category);
    }

    public async Task<PagedResult<ArticleCategoryDto>> GetPagedAsync(PageRequestDto request)
    {
        var query = _categoryRepository.Table
            .Include(c => c.Parent)
            .Include(c => c.Articles)
            .Where(c => !c.IsDeleted)
            .OrderBy(c => c.Sort)
            .ThenBy(c => c.Name);

        var total = await query.CountAsync();
        var items = await query
            .Skip((request.PageIndex - 1) * request.PageSize)
            .Take(request.PageSize)
            .ToListAsync();

        var dtos = new List<ArticleCategoryDto>();
        foreach (var item in items)
        {
            dtos.Add(await MapToDtoAsync(item));
        }

        return new PagedResult<ArticleCategoryDto>
        {
            Items = dtos,
            Total = total,
            PageIndex = request.PageIndex,
            PageSize = request.PageSize
        };
    }

    public async Task<List<ArticleCategoryDto>> GetAllAsync()
    {
        var categories = await _categoryRepository.Table
            .Include(c => c.Parent)
            .Include(c => c.Articles)
            .Where(c => !c.IsDeleted)
            .OrderBy(c => c.Sort)
            .ThenBy(c => c.Name)
            .ToListAsync();

        var dtos = new List<ArticleCategoryDto>();
        foreach (var category in categories)
        {
            dtos.Add(await MapToDtoAsync(category));
        }

        return dtos;
    }

    public async Task<List<ArticleCategoryDto>> GetTreeAsync()
    {
        var allCategories = await GetAllAsync();
        return BuildTree(allCategories, null);
    }

    public async Task<List<ArticleCategoryDto>> GetChildrenAsync(Guid? parentId)
    {
        var categories = await _categoryRepository.Table
            .Include(c => c.Parent)
            .Include(c => c.Articles)
            .Where(c => c.ParentId == parentId && !c.IsDeleted)
            .OrderBy(c => c.Sort)
            .ThenBy(c => c.Name)
            .ToListAsync();

        var dtos = new List<ArticleCategoryDto>();
        foreach (var category in categories)
        {
            dtos.Add(await MapToDtoAsync(category));
        }

        return dtos;
    }

    public async Task<ArticleCategoryDto> CreateAsync(CreateArticleCategoryDto createDto)
    {
        var category = new ArticleCategory
        {
            Id = Guid.NewGuid(),
            Name = createDto.Name,
            Description = createDto.Description,
            ParentId = createDto.ParentId,
            Sort = createDto.Sort,
            CreatedAt = DateTime.UtcNow,
            UpdatedAt = DateTime.UtcNow,
            IsDeleted = false
        };

        var createdCategory = await _categoryRepository.CreateAsync(category);

        // 重新查询以获取导航属性
        var result = await _categoryRepository.Table
            .Include(c => c.Parent)
            .Include(c => c.Articles)
            .FirstAsync(c => c.Id == createdCategory.Id);

        return await MapToDtoAsync(result);
    }

    public async Task<ArticleCategoryDto> UpdateAsync(UpdateArticleCategoryDto updateDto)
    {
        var category = await _categoryRepository.GetByIdAsync(updateDto.Id);
        if (category == null || category.IsDeleted)
            throw new ArgumentException("ArticleCategory not found");

        // 检查是否设置父分类为自己或自己的子分类
        if (updateDto.ParentId.HasValue && await IsCircularReferenceAsync(updateDto.Id, updateDto.ParentId.Value))
            throw new ArgumentException("Cannot set parent to self or child category");

        category.Name = updateDto.Name;
        category.Description = updateDto.Description;
        category.ParentId = updateDto.ParentId;
        category.Sort = updateDto.Sort;
        category.UpdatedAt = DateTime.UtcNow;

        await _categoryRepository.UpdateAsync(category);

        // 重新查询以获取导航属性
        var result = await _categoryRepository.Table
            .Include(c => c.Parent)
            .Include(c => c.Articles)
            .FirstAsync(c => c.Id == category.Id);

        return await MapToDtoAsync(result);
    }

    public async Task DeleteAsync(Guid id)
    {
        var category = await _categoryRepository.GetByIdAsync(id);
        if (category == null || category.IsDeleted)
            throw new ArgumentException("ArticleCategory not found");

        // 检查是否有子分类
        if (await HasChildrenAsync(id))
            throw new InvalidOperationException("Cannot delete category with children");

        // 检查是否有文章
        if (await HasArticlesAsync(id))
            throw new InvalidOperationException("Cannot delete category with articles");

        await _categoryRepository.DeleteAsync(category);
    }

    public async Task<bool> HasChildrenAsync(Guid id)
    {
        return await _categoryRepository.Table
            .AnyAsync(c => c.ParentId == id && !c.IsDeleted);
    }

    public async Task<bool> HasArticlesAsync(Guid id)
    {
        return await _articleRepository.Table
            .AnyAsync(a => a.CategoryId == id && !a.IsDeleted);
    }

    /// <summary>
    /// 检查是否存在循环引用 (防止将父分类设置为自己或自己的子分类)
    /// </summary>
    /// <param name="categoryId">当前分类ID</param>
    /// <param name="parentId">要设置的父分类ID</param>
    /// <returns>是否存在循环引用</returns>
    private async Task<bool> IsCircularReferenceAsync(Guid categoryId, Guid parentId)
    {
        if (categoryId == parentId)
            return true;

        var parent = await _categoryRepository.GetByIdAsync(parentId);
        while (parent?.ParentId != null)
        {
            if (parent.ParentId == categoryId)
                return true;
            parent = await _categoryRepository.GetByIdAsync(parent.ParentId.Value);
        }

        return false;
    }

    /// <summary>
    /// 构建树形结构
    /// </summary>
    /// <param name="categories">所有分类列表</param>
    /// <param name="parentId">父分类ID</param>
    /// <returns>树形分类列表</returns>
    private static List<ArticleCategoryDto> BuildTree(List<ArticleCategoryDto> categories, Guid? parentId)
    {
        return categories
            .Where(c => c.ParentId == parentId)
            .Select(c =>
            {
                c.Children = BuildTree(categories, c.Id);
                return c;
            })
            .ToList();
    }

    /// <summary>
    /// 将分类实体映射为DTO
    /// </summary>
    /// <param name="category">分类实体</param>
    /// <returns>分类DTO</returns>
    private async Task<ArticleCategoryDto> MapToDtoAsync(ArticleCategory category)
    {
        var articleCount = await _articleRepository.Table
            .CountAsync(a => a.CategoryId == category.Id && !a.IsDeleted);

        return new ArticleCategoryDto
        {
            Id = category.Id,
            Name = category.Name,
            Description = category.Description,
            ParentId = category.ParentId,
            Sort = category.Sort,
            CreatedTime = category.CreatedAt,
            UpdatedTime = category.UpdatedAt,
            ParentName = category.Parent?.Name,
            ArticleCount = articleCount
        };
    }
}
